#ifndef _DYNLINK_H
#define _DYNLINK_H

//! The way to use this library and header is described at
//! http://www.codeproject.com/KB/DLL/dynlink.aspx
//! In short:
//! You have to edit the dll header. Add the following lines to the changed 
//! header file:
//!
//! #undef  DYNLINK_LIBNAME
//! #define DYNLINK_LIBNAME <libname>
//! #include <dynlink.h>
//! 
//! where <libname> is the name of the dll (e.g. psapi). This need not be the
//! name of the dll on disk as you can specify alternative filenames to the 
//! CDynlinkLib::load() function.
//! 
//! Also, change function declarations:
//!		rettype callspec funcname(t1 par1, t2 par2, ...)
//! into
//!		DYNLINK_DECLARE(rettype, callspec, funcname, (t1 par1, t2 par2, ...))
//! 
//! And variable declarations (if exported by the dll):
//!		type var;
//! into
//!		DYNLINK_DECLARE_VAR(type, var)
//! 
//! In case you want to use stubs you can use DYNLINK_DECLARE_EX and 
//! DYNLINK_DECLARE_VAR_EX and specify a custom return value / custom value of
//! the variable that is returned when the symbol could not be loaded. 
//! If not given, the DYNLINK_RETVAL_<type> macro is used (it has to be defined
//! by you before using DYNLINK_DECLARE if you define DYNLINK_USE_STUBS

//! Versions:
//! 2011/07/12 (AS): Initial version
#include <string>
#include <vector>
#include <typeinfo>

// Helpers for stringifying expanded macro contents
#define DYNLINK_STR(s) DYNLINK_ST_(s)
#define DYNLINK_ST_(s) #s

// Helpers for concatenating expanded macro contents
#define DYNLINK_JOIN(x, y) DYNLINK_JOI_(x, y)
#define DYNLINK_JOI_(x, y) x ## y

// Return value for the CDynlinkLib::load() and CDynlinkLib::get_state() 
// functions.

//! No error occurred
#define DYNLINK_SUCCESS            0
//! Library not loaded, LoadLibrary() failed
#define DYNLINK_ERR_LOADLIBRARY    1
//! Library loaded but some of the symbols where not found and are set to null 
//! pointers
#define DYNLINK_ERR_NULLPTRS       2
//! Library loaded and some symbols were not found and replaced by their stubs
#define DYNLINK_ERR_STUBS          3
//! The library was not loaded yet
#define DYNLINK_ERR_NOTLOADED      4
//! Invalid loading method for this task (e.g. rename or stubs using implicit
//! linking
#define DYNLINK_ERR_INVALID_METHOD 5

#endif 

// The macos for wrapping variable and function delcarations which are alsway
// re-defined in this file depending on whether DYNLINK_USE_STUBS, DYNLINK_DEFINE 
// and DYNLINK_LINK_IMPLICIT are defined.

//! DYNLINK_DECLARE(type, callspec, func, (arglist))
//! Declare a function of type:
//!		type (callspec * func)(arglist);
//! When using stubs this macro assumes that DYNLINK_RETVAL_<retval> is defined
//! and use it as the return value of the stub function. 
#undef DYNLINK_DECLARE

//! DYNLINK_DECLARE(type, callspec, func, (arglist), retval)
//! Same as DYNLINK_DECLARE but also sets a custom return value for only this
//! function. Again, you can use this to e.g. have a function called whenever
//! the stub is accessed.
#undef DYNLINK_DECLARE_EX

//! DYNLINK_DECLARE_VAR(type, name)
//! Declare a variable of type:
//!		type name;
//! When using stubs, this macro uses DYNLINK_RETVAL_<type> as the initial 
//! value of the stub variable (but changes it when the variable is set by the
//! code.)
#undef DYNLINK_DECLARE_VAR

//! DYNLINK_DECLARE_VAR_EX(type, name, value)
//! Same as DYNLINK_DECLARE_VAR but also sets the initial value of this 
//! variable to a custom value.
#undef DYNLINK_DECLARE_VAR_EX

//! Definitions to define (or not) in the modified header that change how the
//! wrapper behaves.

//!#define DYNLINK_NO_CHECK
//! If this is defined using dynlink should be as fast as using traditional
//! function pointers as no additional checks are performed. Calling stubs is 
//! still possible but when stubs are not enabled and the library or symbol 
//! were not loaded, the program crashes.
//! By default (i.e. if this is not defined) the operator casting the 
//! DynlinkSymbols to a function pointer or variable reference checks whether
//! the symbol was successfully loaded and can actions that are configured 
//! individually for each library. 
//! See CDynlinkLib::set_throw() and CDynlinkLib::set_auto_load() for details.
//! Other customized behaviour, including counting of calls etc. may later be 
//! added.

//!#define DYNLINK_USE_STUBS
//! If this is defined, stub functions are defined for each function and 
//! variable and their address is assigned to the function pointers in case 
//! the functions cannot be loaded.

//! The default return values of stub functions are given by macros of the form
//! DYNLINK_RETVAL_<type>. e.g. 
//! 
//!		#define DYNLINK_RETVAL_int -999
//! 
//! will cause every stub function with return type 'int' to return -999. 
//! There is no other way on the calling side whether you were calling the, 
//! unless you add additional code. 
//! As a remark: if you want the stub function to do more than just return a 
//! value, nothing stops you from defining something like
//! 
//!		#define DYNLINK_RETVAL_int (some_function())
//! 
//! In fact you cann even implement callbacks this way as the stub function
//! has a local variable called p_symbol which is a pointer to the symbol
//! just acessed:
//! Thus:
//! 
//!		#define DYNLINK_RETVAL_int (some_function(p_symbol))
//! 
//! There is one pitfall: when the return type contains spaces or special 
//! charaters (e.g. unsigned int &) the default macro will fail miserably.
//! You have two options: 
//! Use a typedef in your declarations: (e.g. typedef unsigned int & UINT_REF;)
//! which allows you to define DYNLINK_RETVAL_UINT_REF or use the 
//! DYNLINK_DECLARE_EX macro to avoid the default return value.

//! Only the void return value is pre-defined, all others should be defined in
//! your modified header file
#define DYNLINK_RETVAL_void

//!#define DYNLINK_LIBNAME
//! In the 'definition' file (see below) this will define a function called
//! dynlink_XXX() which returns a reference to a CDynlinkLib object with XXX 
//! being the value of the macro. You load and free the library by calling 
//! dynlink_XXX().load() and dynlink_XXX().free(). See the class declaration 
//! below for details.

// Definitions to define (or not) in the files including the modified header:

//!#define DYNLINK_PREFIX
//! Prefixes every declaration e.g. to load two versions of a library at the
//! same time and call their functions depending on which one was loaded.

//!#define DYNLINK_LINK_IMPLICIT
//! This determines the linking method to be implicit (aka static) linking. 
//! Once you have modified the header there is no real reason to use this
//! but except for checking your code against a new version of the dll.
//! NOTE: if you have DYNLINK_PREFIX defined, you will not be able to use 
//! this. 
//! This must be defined (or not defined) wherever the same modified header
//! is included with the same prefix.

//!#define DYNLINK_DEFINE
//! Define this in exactly one .c/.cpp file per module and prefix before 
//! including <dynlink.h> to include function definitions, and function 
//! pointer definitions. In all other files DO NOT define this or you get 
//! duplicate symbol definitions

#ifndef DYNLINK_CALLSPEC
#	define DYNLINK_CALLSPEC WINAPI
#endif

#ifndef DYNLINK_CLASSDEF
#define DYNLINK_CLASSDEF

class CDynlinkBase
{
public:
	//! Get error during library / symbol load. 
	//! @return 
	//!		The last system error that caused a call to this object to fail
	virtual DWORD get_error() const = 0;
	
	//! Get error during library / symbol load. 
	//! @param s_err
	//!		Error string
	//! @return 
	//!		The last system error that caused a call to this object to fail
	DWORD get_error(std::wstring &s_err) const;
	DWORD get_error(std::string &s_err) const;

	//! True if the symbol/library was successfully loaded with no stub
	//! in place.
	virtual bool is_loaded() const = 0;
	//! True if the symbol/library was successfully loaded, possibly with 
	//! stubs in place
	virtual bool is_loaded_or_stub() const = 0;

	//! Format error message from error code as returned by get_error
	static void get_error(DWORD dwErr, std::wstring &s_err);
	static void get_error(DWORD dwErr, std::string &s_err);
};

//! A symbol in the dll 
class CDynlinkSymbol : public CDynlinkBase
{
	friend class CDynlinkLib;
public:
	// Constructor. Don't use this unless you know exactly what you are doing. 
	// This is used by the DYNLINK_DECLARE macros
	CDynlinkSymbol(CDynlinkLib &lib, LPCSTR s_name);
	// Overwrite
	virtual DWORD get_error() const;
	
	//! True of the symbol was successfully loaded
	virtual bool is_loaded() const
	{
		return (m_p_ptr != NULL) && (m_p_ptr != m_p_stub);
	}
	//! True if the symbol was successfully loaded or a stub is in place
	virtual bool is_loaded_or_stub() const
	{
		return (m_p_ptr != NULL);
	}
	//! Get the symbol name
	LPCSTR get_name()
	{
		return m_s_name.c_str();
	}
	//! Get the library from which we are loaded
	CDynlinkLib * get_lib()
	{
		return m_p_lib;
	}
protected:
	// Check for custom actions
	void * check();
	// Called when lib unloads. Resets to NULL pointer and clears all error
	// flags.
	void free();
	// Try to load the symbol. If hDll == ((HMODULE)-1) load the stub but behave
	// as if it was the symbol
	bool load(HMODULE hDll);
	// Name in the library. Ordinals currently not supported
	std::string m_s_name;
	// Where to store the pointer returned by GetProcAddress()
	void * m_p_ptr;
	// The stub pointer to replace the pointer with
	void * m_p_stub;
	// The error code returned by GetProcAddress(). Is ERROR_SUCCESS if the
	// loading failed
	DWORD m_dwError;	
	// Pointer to the dynlink library
	CDynlinkLib * m_p_lib;
};

//! Class to simulate a variable imported from a dll. T is the type of the
//! variable i.e. m_p_ptr and m_p_stub are both pointers to a variable of
//! type T.
template<class T> class CDynlinkVar : public CDynlinkSymbol
{
public:
	// Create and set the stub value to be returned
	CDynlinkVar(CDynlinkLib &lib, LPCSTR s_name, const T&val) : CDynlinkSymbol(lib, s_name)
	{
		m_stub_value = val;
		m_p_stub = (void *) &(m_stub_value);
		m_p_ptr = NULL;
	}
	// Create without stub
	CDynlinkVar(CDynlinkLib &lib, LPCSTR s_name) : CDynlinkSymbol(lib, s_name)
	{
		m_p_stub = NULL;
		m_p_ptr  = NULL;
	}
	// Use as variable of type T. 
	operator T&()	
	{ 
#ifndef DYNLINK_NO_CHECK
		return *(T* ) check();
#else
		return *(T*) m_p_ptr; 
#endif
	}
protected:
	// Storage for the stub variable.
	T m_stub_value;
};

//! A function. T is the type of the function pointer, that is m_p_ptr and 
//! m_p_stub are both functio pointers
template<class T> class CDynlinkFunc : public CDynlinkSymbol
{
public:
	// Create and set the stub value to be returned
	CDynlinkFunc(CDynlinkLib &lib, LPCSTR s_name, T stub = NULL) : CDynlinkSymbol(lib, s_name)
	{
		m_p_stub = (void *) stub;
		m_p_ptr = NULL;
	}
	// Use as variable of type T
	operator T()	
	{ 
#ifndef DYNLINK_NO_CHECK
		return (T) check();
#endif
		return (T) m_p_ptr; 
	}
};

//! Interface class for explicit and implicit linking and loading of dlls
//! and contained symbols.
class CDynlinkLib : public CDynlinkBase
{
	friend class CDynlinkSymbol;
public:
	//! Mode of loading
	enum mode
	{
		//! Explicit loading of function addresses
		ex,
		//! Explicit loading, does not fail when a function symbol is
		//! not found and creates a stub instead that will be called and 
		//! executes the code defined in DYNLINK_IFABSENT_<rettype> instead
		exstub,
		//! Loading is assumed to be implicit. Function returns always true
		imp
	};

	//! Create a library interface. Use this only if you know EXACTLY what
	//! you are doing. Usually including the modified header will take care 
	//! of construction and defines a function dynlink_<name>() to access
	//! it as a singleton where <name> is the library name defined in the 
	//! header.
	//! @param sName
	//!		The name of the library. (e.g. psapi for psapi.dll)
	//! @param d_mode
	//!		The mode (see above). If this is 'imp' or 'delay' it is assumed
	//!		that the appropriate linker flags were set.
	CDynlinkLib(LPCWSTR sName, mode d_mode = ex);
	CDynlinkLib(LPCSTR  sName, mode d_mode = ex);
	
	//! Get error during library load
	//! @param s_err
	//!		The string to write a formatted error message to
	//! @return 
	//!		The error as returned by GetLastError() during the load_library call.
	virtual DWORD get_error() const;
	
#ifndef DYNLINK_NO_CHECK		
	//! If set to true accessing a variable or function not loaded 
	//! will cause a c++ exception to be thrown. In fact, the symbol throws a 
	//! pointer to itself so you could wrap your code accessing the lib into
	//! try 
	//! {
	//!		code;
	//! }
	//! catch (CDynlinkSymbol * p)
	//!	{
	//!		retrive and display error information;
	//! }
	//! @param b_throw
	//!		When to throw the exception
	//!		0: never throw exceptions (default behaviour)
	//!		1: throw exceptions when the function is not loaded (loadable)
	//!		   and no stub is available
	//!		2: throw exceptioin alsways, even if a stub would be available
	//! 
	//! REMARK: this will also occur if you just check the symbol against 
	//! NULL. If this is enabled use the is_loaded() function instead to 
	//! avoid throwing an exception.
	void set_throw(int d_throw)
	{
		m_d_throw = d_throw;
	}

	//! Change auto load behavior. If enabled, the library will load on first 
	//! access to one of its symbols.
	void set_auto_load(bool b_auto_load)
	{
		m_b_auto_load = b_auto_load;
	}
#endif
	//! Load the library and all symbols. For details see above.
	//! @param b_retry
	//!		Retry the load independently of previous results. If not TRUE 
	//!		a loaded library and its symbols will not be touched.
	//!	@param s_name
	//!		Replace the library name given above.
	//! @return 
	//!		State of the loading, see definitions of DYNLINK_ERR ... above
	int load(LPCWSTR s_name = 0, bool b_retry = false);
	int load(LPCSTR  s_name, bool b_retry = false);
	
	//! Fake-load the library and use only stubs. This will not call 
	//! LoadLibrary() and it will not call GetProcAddress() but all error 
	//! handling will indicate that the library was successfully loaded.
	//! This function has no effect if loading is static.
	int load_stubs();

	//! Get loading state.
	//! @return 
	//!		State of the loading, see definitions of DYNLINK_ERR ... above
	int get_state() const
	{
		return m_d_loaded;
	}
	//! Get the loading mode of the library object. See CDynlinkLib::mode for
	//! details.
	//! @return
	//!		the mode with which the library was loaded.
	mode get_mode() const
	{
		return m_mode;
	}
	//! Check whether the library was loaded successfully.
	//! @return
	//!		true if get_state() is DYNLINK_SUCCESS
	virtual bool is_loaded() const
	{
		return (get_state() == DYNLINK_SUCCESS);
	}

	//! Check whether all symbols can be called in the library without NULLPTR
	//! references.
	//! @return
	//!		true if get_state() is either DYNLINK_SUCCESS or DYNLINK_ERR_STUBS
	virtual bool is_loaded_or_stub() const
	{
		return (get_state() == DYNLINK_SUCCESS) || (get_state() == DYNLINK_ERR_STUBS);
	}
	
	//! Unload the library. This will fail for an implicitely linked library
	//! but should succeed for explicite linkage and delayed loading.
	bool free();

	//! Get the module handle for a successfully loaded library. NULL if the 
	//! library was not loaded.
	//! REMARK: when implicitely linking, this may return NULL even if the 
	//! library was successfully loaded but if the module name does not match
	HMODULE get_module() const
	{
		return m_h_dll;
	}

	//! Get the filename of the library loaded. Can fail for implicitely linked
	//! libs if the library name is not correct
	bool get_filename(std::wstring &s_fn) const;
	//! Get the filename of the library loaded.
	bool get_filename(std::string &s_fn) const;
protected:		
	// Add a symbol.
	void add(CDynlinkSymbol * p_sym);
	// Case corrected name, this is used together with the delay load functions
	// which only understand the LPCSTR variant (TODO: check this)
	std::wstring m_s_realname;
	// The name passed into the constructor of the Load() function
	std::wstring m_s_name;
	// Backup of original name
	std::wstring m_s_name_bak;
	// The handle
	HMODULE m_h_dll;
	// The link mode. This cannot be changed
	mode m_mode;
	// Whether the library is loaded at the moment
	int m_d_loaded;
	// List of symbols
	std::vector<CDynlinkSymbol *> m_vecSymbols;
	// Error of LoadLibrary
	DWORD m_dwError;
	// Throw if null pointer
	int m_d_throw;
	// Auto load enabled or disabled
	bool m_b_auto_load;
private:
	void init(const std::wstring& s_name, mode d_mode);
};

//! Encloses an object to return the appropriate error and status information
//! both when explicitely loaded (smybols are CDynlinkSymbol derived) or when 
//! implicitely loaded (symbols are simple variables or function pointers).
//! If sure that always explicitly loading a section this is unnecessary and
//! one can access the member functions directly. 
//! However, as not to clutter the above classes, formated errors are available 
//! only through here.
class CDynlinkGetErr : public CDynlinkBase
{
public:
	CDynlinkGetErr(const CDynlinkBase& symbol)
	{
		m_b_obj = &symbol;
	}
	template<class T> CDynlinkGetErr(const T* symbol)
	{
		m_b_obj = NULL;
	}

	DWORD get_error() const
	{
		return m_b_obj ? m_b_obj ->get_error() : ERROR_SUCCESS;
	}

	bool is_loaded() const
	{
		return m_b_obj ? m_b_obj ->is_loaded() : true;
	}

	bool is_loaded_or_stub() const
	{
		return m_b_obj ? m_b_obj ->is_loaded_or_stub() : true;
	}
protected:
	const CDynlinkBase * m_b_obj;
};

#endif

#ifndef COMPILING_DYNLINK

#undef _P
#undef _S
#undef _G

#ifdef DYNLINK_PREFIX
    // Can only be used when linkin explicitely
#    ifdef DYNLINK_LINK_IMPLICIT
#        error Cannot use prefix when not linking explicitely!
#    endif
    // Define the way names are prefixed
#    define _P(xx) DYNLINK_JOIN(DYNLINK_PREFIX, xx)
#else
#    define _P(xx) xx
#endif

#undef  DYNLINK_GETOBJ
#define DYNLINK_GETOBJ \
        DYNLINK_JOIN(dynlink_, _P(DYNLINK_LIBNAME))

// If explicitely linking, declaration differs from that of the definition file as
#ifdef DYNLINK_LINK_IMPLICIT
     // just a regular declaration if we are not using explicit linking
#    define DYNLINK_DECLARE(ret, callspec, func, args) \
        __declspec(dllimport) ret callspec func##args;
#    define DYNLINK_DECLARE_VAR(type, name) \
        __declspec(dllimport) type name;
#else /* DYNLINK_LINK_IMPLICIT */
#	ifndef DYNLINK_DEFINE
		// If this is not the file that is responsible for loading, we declare
		// as an external pointer to the function. We may have to include
		// approprate specifiers or even a customization macro at some point
#		define DYNLINK_DECLARE(ret, callspec, func, args) \
			extern CDynlinkFunc<ret (callspec *) args> _P(func);
#		define DYNLINK_DECLARE_VAR(type, name) \
			extern CDynlinkVar<type> _P(name); 
#	endif
#endif

#ifndef DYNLINK_DEFINE

// Access to the library object
CDynlinkLib &DYNLINK_GETOBJ();

#else /* DYNLINK_DEFINE */ 

#define _S(xx) DYNLINK_JOIN(__dynlink, _P(xx))
#define _G(xx) DYNLINK_JOIN(__dynlink_stub, _P(xx))

#ifndef DYNLINK_NO_AUTO
#ifndef _DEBUG
#   pragma comment(lib, "dynlink.lib")
#else
#   pragma comment(lib, "dynlinkd.lib")
#endif
#endif
    
#define DYNLINK_DLL \
    DYNLINK_STR(DYNLINK_LIBNAME) ".dll"
#define DYNLINK_LIB \
    DYNLINK_STR(DYNLINK_LIBNAME) ".lib"

#define DYNLINK_LIBOBJ(name, mode) \
    CDynlinkLib &DYNLINK_GETOBJ () { static CDynlinkLib _lib(name, mode); return _lib; };
        
#ifdef DYNLINK_LINK_IMPLICIT
// Implicit linking. Everything is very easy

#    pragma message("Automatically linking with " DYNLINK_LIB ".")
#    pragma comment(lib, DYNLINK_LIB)
     DYNLINK_LIBOBJ(DYNLINK_STR(DYNLINK_LIBNAME), CDynlinkLib::imp)

#else /* DYNLINK_LINK_IMPLICIT */

    // This is the file responsible for loading. i.e. we declare the function
    // pointer and the object which is put into the list and the load() function
    // of which results in loading through GetProcAddress
#   ifndef DYNLINK_USE_STUBS
        // Declare without stubs, i.e. not loading a function results in a NULL 
        // pointer. 

#       define DYNLINK_DECLARE(ret, callspec, func, args) \
            CDynlinkFunc<ret (callspec *) args> _P(func)(DYNLINK_GETOBJ(), #func); 
#       define DYNLINK_DECLARE_VAR(type, name) \
            CDynlinkVar<type> _P(name)(DYNLINK_GETOBJ(), #name); 
        //  The dynlinklib object
        DYNLINK_LIBOBJ(DYNLINK_STR(DYNLINK_LIBNAME), CDynlinkLib::ex)

#   else /* DYNLINK_USE_STUBS */
        // Declare including a definition of a stub, i.e. pass it to the loader 
        // so it will put this pointer instead if loading fails

#       define DYNLINK_DECLARE_EX(ret, callspec, func, args, retval) \
            static ret callspec _G(func)##args;           \
            CDynlinkFunc<ret (callspec *) args> _P(func)(DYNLINK_GETOBJ(), #func, &_G(func)); \
            static ret callspec _G(func)##args            \
            {                                             \
			    CDynlinkSymbol * p_symbol = &_P(func);    \
                return retval;                            \
            }                                             \
            
#       define DYNLINK_DECLARE_VAR_EX(type, name, value)  \
            CDynlinkVar<type> _P(name)(DYNLINK_GETOBJ(), #name, value); 

#       define DYNLINK_DECLARE(ret, callspec, func, args) \
            DYNLINK_DECLARE_EX(ret, callspec, func, args, DYNLINK_JOIN(DYNLINK_RETVAL_, ret));
#       define DYNLINK_DECLARE_VAR(type, name)            \
            DYNLINK_DECLARE_VAR_EX(ret, func, args, DYNLINK_JOIN(DYNLINK_RETVAL_, type));

        //  The dynlinklib object
        DYNLINK_LIBOBJ(DYNLINK_STR(DYNLINK_LIBNAME), CDynlinkLib::exstub);

#   endif /* DYNLINK_USE_STUBS */

#endif /* DYNLINK_LINK_IMPLICIT */
#endif /* DYNLINK_DEFINE */ 

// if this is not the definition file or stubs are not enabled these are set to
// the regular macros
#ifndef DYNLINK_DECLARE_EX
#    define DYNLINK_DECLARE_EX(ret, callspec, func, args, retval) \
        DYNLINK_DECLARE(ret, callspec, func, args)
#    define DYNLINK_DECLARE_VAR_EX(type, name, value) \
        DYNLINK_DECLARE_VAR(type, name)
#endif

#endif /* !COMPILING_DYNLINK */

// undefine this as if we are included again in the same file, this is either to
// be re-defined or we switch to declare-only, possibly other linkage etc
#undef DYNLINK_LIB
#undef DYNLINK_DLL
#undef DYNLINK_LIBOBJ
